home *** CD-ROM | disk | FTP | other *** search
/ BCI NET / BCI NET Dec 94.iso / archives / programming / utilities / exdat100.lha / xd / xd.c < prev    next >
Encoding:
C/C++ Source or Header  |  1994-01-28  |  19.4 KB  |  842 lines

  1. /* Copyright © 1994 Cedric Beust. See file COPYRIGHT for more information */
  2. /* 
  3.  * xd.c
  4.  *
  5.  * $Id: xd.c,v 1.15 1994/01/27 22:56:35 beust Exp beust $
  6.  */
  7.  
  8. /*
  9.  ** Do not rely on this format, it might evolve in future versions.
  10.  ** It is presented here for a simple personal reminder purpose .
  11.  **
  12.  ** All strings are written as "BSTR" (i.e. preceded by their size
  13.  ** on a LONG byte).
  14.  
  15.  LIST XDAT
  16.    PROP XSHA            Hold all the shared information
  17.      XINF               Information on this file
  18.        typeSize type
  19.        applicationSize application
  20.        authorSize author
  21.        versionSize version
  22.        dateSize date
  23.      XFIE                       Give all fields with their types (private)
  24.        fieldType fieldSize fieldName
  25.        fieldType fieldSize fieldName
  26.        ...
  27.      XUSE                       User-defined key/value pairs (public)
  28.        key valueSize value
  29.        ...
  30.    FORM XCON                       Contents of the records
  31.        XREC fieldSize fieldValue fieldSize fieldValue ...    a record
  32.        XREC fieldSize fieldValue fieldSize fieldValue ...    another record
  33.  */
  34.  
  35.  
  36. #include "xd.h"
  37.  
  38. #define MWDEBU
  39.  
  40. #define NEW(v, t) v = (t *) malloc(sizeof(t))
  41. #define STREQUAL(s1, s2) (strcmp(s1, s2) == 0)
  42.  
  43.  
  44. /**********************************************************************/
  45.  
  46. /* Associate a field name and its type */
  47. struct TypeAssoc {
  48.    struct Node node;
  49.    Xd_Type type;
  50.    void *value;
  51. };
  52.  
  53. /* Structure for the database */
  54. typedef struct _Xd_Database {
  55.    struct IFFHandle *iff;   /* the iff handle */
  56.    struct List *ltypes;    /* list of types */
  57.    struct List *lshared;  /* list of shared keys/values */
  58.    int writeOccured;     /* if 1, a write has occured, can't declare more */
  59.    char *fileType;
  60.    char *application;
  61.    char *author;
  62.    char *version;
  63.    char *date;
  64.    Xd_Error errorNumber;
  65.    char *errorInfo;
  66. };
  67.  
  68. /*
  69.  ** Here are all the new chunk names we use.
  70.  **
  71.  ** XDAT (xdata)    is the main type of our IFF chunk
  72.  ** XSHA (xshared   is the id of the PROP (shared) chunk
  73.  **   XINF (xinfo)  is used in the PROP chunk to hold information about the file
  74.  **   XFIE (xfield)   is used in the PROP chunk to give names and types to fields
  75.  **   XUSE (xuser)    is used in the PROP chunk for user-defined keys/values
  76.  ** XCON (xcontent) introduces the chunk containing all the records
  77.  **   XREC (xrecord)  introduces a new record
  78.  */
  79.  
  80. #define ID_XDAT MAKE_ID('X', 'D', 'A', 'T')
  81. #define ID_XSHA MAKE_ID('X', 'S', 'H', 'A')
  82. #define ID_XINF MAKE_ID('X', 'I', 'N', 'F')
  83. #define ID_XFIE MAKE_ID('X', 'F', 'I', 'E')
  84. #define ID_XUSE MAKE_ID('X', 'U', 'S', 'E')
  85. #define ID_XCON MAKE_ID('X', 'C', 'O', 'N')
  86. #define ID_XREC MAKE_ID('X', 'R', 'E', 'C')
  87.  
  88. struct IFFParseBase *IFFParseBase = NULL;
  89.  
  90. /***********************************************************************
  91.  * Private
  92.  ***********************************************************************/
  93.  
  94. #define SAFEFREE(p) if (p != NULL) free(p)
  95.  
  96. static void
  97. xd_error(Xd_Database xd, int err, char *info)
  98. {
  99.    xd -> errorNumber = (Xd_Error) err;
  100.    SAFEFREE(xd -> errorInfo);
  101.    if (info != NULL)
  102.      xd -> errorInfo = strdup(info);
  103.    else
  104.      xd -> errorInfo = NULL;
  105. }
  106.  
  107. #ifdef A
  108. static void
  109. xd_displayList(struct List *list)
  110. {
  111.    struct  Node *node;
  112.  
  113.    printf("content of list %x\n-----\n", list);
  114.    for (node = list -> lh_Head; node -> ln_Succ; node = node -> ln_Succ) {
  115.       struct TypeAssoc *ta = (struct TypeAssoc *) node;
  116.       if (ta) {
  117.      printf("'%s' -> ", ta -> node.ln_Name);
  118.      if (ta -> value && ta -> type == XD_STRING)
  119.        printf("'%s'\n", ta -> value);
  120.      else if (ta -> value && ta -> type == XD_INTEGER)
  121.        printf("$%x\n", ta -> value);
  122.       }
  123.    }
  124.    printf("-----\n");
  125. }
  126. #endif
  127.  
  128. static void
  129. xd_freeList(struct List *list)
  130. /* Free a list of TypeAssoc nodes */
  131. {
  132.    struct Node *node;
  133.    for (node = list -> lh_Head; node -> ln_Succ; node = node -> ln_Succ) {
  134.       struct TypeAssoc *ta = (struct TypeAssoc *) node;
  135.       if (ta != NULL) {
  136.      SAFEFREE(ta -> value);
  137.      SAFEFREE(ta -> node.ln_Name);
  138.      free(ta);
  139.       }
  140.    }
  141.    free(list);
  142. }
  143.  
  144. static long
  145. xd_makeId(char *name)
  146. {
  147.    int i, j;
  148.    char chunk[4];
  149.    long result;
  150.  
  151.    if (strlen(name) >= 4) {
  152.       for (i=0; i<4; i++) chunk[i] = name[i];
  153.    }
  154.    else {
  155.       for (i=0; i<4-strlen(name); i++) chunk[i] = name[0];
  156.       for (j = 0; i<4; i++, j++) chunk[i] = name[j];
  157.    }
  158.  
  159.    result = MAKE_ID(chunk[0], chunk[1], chunk[2], chunk[3]);
  160.    return result;
  161. }
  162.  
  163. static int
  164. xd_declareTypeAssoc(Xd_Database xd, char *field, char *value,
  165.             Xd_Type type, struct List *list)
  166. {
  167.    struct TypeAssoc *ta;
  168.    int result = 0;
  169.  
  170.    /* Check if this new field won't collide with another one */
  171.    if (FindName(list, field)) {
  172.       xd_error(xd, XD_FIELD_ALREADY_EXISTS, field);
  173.       result = 1;
  174.    }
  175.  
  176.    else {
  177.       if (! xd -> writeOccured) {
  178.      NEW(ta, struct TypeAssoc);
  179.      ta -> node.ln_Name = strdup(field);
  180.      ta -> type = type;
  181.      ta -> value = value;
  182.      AddTail(list, (struct Node *) ta);
  183.       }
  184.       else
  185.     result = 1;
  186.    }
  187.  
  188.    return result;
  189. }
  190.  
  191. static int
  192. xd_declareShared(Xd_Database xd, char *key, char *value)
  193. {
  194.    return xd_declareTypeAssoc(xd, key, value, XD_STRING, xd -> lshared);
  195. }
  196.  
  197. static void
  198. xd_writeChunk(struct IFFHandle *iff, void *bytes, int l)
  199. /* Write the size, then the bytes, and the possible pad bytes */
  200. {
  201.    char pad[4];
  202.  
  203.    pad[0] = pad[1] = pad[2] = pad[3] = 0;
  204.  
  205.    if (bytes == NULL) l = 0;
  206.    WriteChunkBytes(iff, (char *) & l, sizeof(l));
  207.    if (bytes) WriteChunkBytes(iff, bytes, l);
  208.    if (l % 4) {
  209.       WriteChunkBytes(iff, pad, 4 - (l % 4));
  210.   }
  211. }
  212.  
  213. static struct TypeAssoc *
  214. xd_findType(struct List *ltypes, char *field)
  215. {
  216.    struct TypeAssoc *result = NULL;
  217.  
  218.    result = (struct TypeAssoc *) FindName(ltypes, field);
  219.    return result;
  220. }
  221.  
  222. static int
  223. xd_skipToId(Xd_Database xd, long id)
  224. /* Skip to next hunk of id 'id' */
  225. /* Return : 1 if found */
  226. {
  227.    int running = 1;
  228.    int result = 0, error;
  229.    struct ContextNode *cn;
  230.  
  231.    while (running) {
  232.       error = ParseIFF(xd -> iff, IFFPARSE_RAWSTEP);
  233.       if (error == IFFERR_EOF) {
  234.      result = 0;
  235.      running = 0;
  236.       }
  237.       else {
  238.      cn = CurrentChunk(xd -> iff);
  239.      if (! cn) {
  240.         result = 0;
  241.         running = 0;
  242.      }
  243.      else if (cn -> cn_ID == id) {
  244.         result = 1;
  245.         running = 0;
  246.      }
  247.       }
  248.    }
  249.  
  250.    return result;
  251. }
  252.  
  253. static int
  254. xd_skipToType(Xd_Database xd, long type)
  255. /* Skip to next hunk of id 'id' */
  256. /* Return : 1 if found */
  257. {
  258.    int running = 1;
  259.    int result = 0, error;
  260.    struct ContextNode *cn;
  261.  
  262.    while (running) {
  263.       error = ParseIFF(xd -> iff, IFFPARSE_RAWSTEP);
  264.       if (error == IFFERR_EOF) {
  265.      result = 0;
  266.      running = 0;
  267.       }
  268.       else {
  269.      cn = CurrentChunk(xd -> iff);
  270.      if (! cn) {
  271.         result = 0;
  272.         running = 0;
  273.      }
  274.      else if (cn -> cn_Type == type) {
  275.         result = 1;
  276.         running = 0;
  277.      }
  278.       }
  279.    }
  280.  
  281.    return result;
  282. }
  283.  
  284. static void
  285. xd_writeHeader(Xd_Database xd)
  286. /* Write the header of the file */
  287. {
  288.    struct List *list;
  289.    struct Node *node;
  290.  
  291.    /*
  292.     ** The XINF chunk.
  293.     ** This is the constant part of the file where the program stores
  294.     ** its main information : fileType, application, author, version, date
  295.     ** in that order.
  296.     */
  297.  
  298.    PushChunk(xd -> iff, ID_XSHA, ID_XINF, IFFSIZE_UNKNOWN);
  299.    xd_writeChunk(xd -> iff, xd -> fileType, strlen(xd -> fileType));
  300.    xd_writeChunk(xd -> iff, xd -> application, strlen(xd -> application));
  301.    xd_writeChunk(xd -> iff, xd -> author, strlen(xd -> author));
  302.    xd_writeChunk(xd -> iff, xd -> version, strlen(xd -> version));
  303.    xd_writeChunk(xd -> iff, xd -> date, strlen(xd -> date));
  304.    PopChunk(xd -> iff);   /* pop XINF chunk */
  305.  
  306.    /*
  307.     ** The XFIE chunk.
  308.     ** Here we give all the names of the fields, preceded by their
  309.     ** type.
  310.     */
  311.  
  312.    PushChunk(xd -> iff, ID_XSHA, ID_XFIE, IFFSIZE_UNKNOWN);
  313.    list = xd -> ltypes;
  314.    for (node = list -> lh_Head; node -> ln_Succ; node = node -> ln_Succ) {
  315.       struct TypeAssoc *ta = (struct TypeAssoc *) node;
  316.       WriteChunkBytes(xd -> iff, & ta -> type, sizeof(ta -> type));
  317.       xd_writeChunk(xd -> iff, ta -> node.ln_Name, strlen(ta -> node.ln_Name));
  318.    }
  319.    PopChunk(xd -> iff);   /* pop XFIE chunk */
  320.  
  321.    /*
  322.     ** The XUSE chunk
  323.     */
  324.  
  325.    PushChunk(xd -> iff, ID_XSHA, ID_XUSE, IFFSIZE_UNKNOWN);/*start XUSE chunk*/
  326.    list = xd -> lshared;
  327.    for (node = list -> lh_Head; node -> ln_Succ; node = node -> ln_Succ) {
  328.       struct TypeAssoc *ta = (struct TypeAssoc *) node;
  329.       xd_writeChunk(xd -> iff, ta -> node.ln_Name, strlen(ta -> node.ln_Name));
  330.       xd_writeChunk(xd -> iff, ta -> value, strlen(ta -> value));
  331.    }
  332.    PopChunk(xd -> iff);
  333.  
  334.    /*
  335.     ** Start the XCON chunk
  336.     */
  337.  
  338.    PushChunk(xd -> iff, ID_XCON, ID_FORM, IFFSIZE_UNKNOWN);  /* start XCON */
  339. }
  340.  
  341. static Xd_Database
  342. xd_readHeader(Xd_Database xd, char *fileType)
  343. /* Read the header of the file and put it into our lists */
  344. /* Compare the fileType read with the one given */
  345. /* as parameter and return NULL if they don't match exactly */
  346. {
  347.    Xd_Database result = xd;
  348.    if (! xd_skipToType(xd, ID_XSHA)) {
  349.       xd_error(xd, XD_NO_TYPE_XSHA, NULL);
  350.       result = NULL;
  351.    }
  352.    else {
  353.       char devNull[10];   /* buffer to hold pad bytes */
  354.  
  355.       /*
  356.        ** Read the XINF chunk
  357.        ** fileType, application, author, version, date
  358.        ** in that order.
  359.        */
  360.       if (xd_skipToId(xd, ID_XINF)) {
  361.      struct ContextNode *cn = CurrentChunk(xd -> iff);
  362.      int i = 0;
  363.  
  364.      if (! cn) {
  365.         xd_error(xd, XD_NO_ID_XINF, NULL);
  366.         result = NULL;
  367.      }
  368.      else {
  369.         int n, size;
  370.         char *name;
  371.         void *info[5];
  372.  
  373.         info[0] = & xd -> fileType;
  374.         info[1] = & xd -> application;
  375.         info[2] = & xd -> author;
  376.         info[3] = & xd -> version;
  377.         info[4] = & xd -> date;
  378.  
  379.         n = cn -> cn_Size - 4;
  380.         while (n > 0) {
  381.            ReadChunkBytes(xd -> iff, & size, sizeof(APTR));
  382.            n -= sizeof(APTR);
  383.            name = (char *) malloc(size + 1);
  384.            ReadChunkBytes(xd -> iff, name, size);
  385.            name[size] = '\0';
  386.            n -= size;
  387.            if (size % 4 != 0) {
  388.           ReadChunkBytes(xd -> iff, devNull, 4 - (size % 4));
  389.           n -= 4 - (size % 4);
  390.            }
  391.  
  392.            *((char **) info[i]) = strdup(name);
  393.            free(name);
  394.            i++;
  395.         }
  396.      }
  397.       }
  398.       else {
  399.      xd_error(xd, XD_NO_ID_XINF, NULL);
  400.       }
  401.  
  402.       /*
  403.        ** Read the XINF chunk
  404.        */
  405.       if (xd_skipToId(xd, ID_XFIE)) {
  406.      struct ContextNode *cn = CurrentChunk(xd -> iff);
  407.      if (! cn) {
  408.         xd_error(xd, XD_NO_ID_XFIE, NULL);
  409.         result = NULL;
  410.      }
  411.      else {
  412.         int n, size;
  413.         Xd_Type type;
  414.         char *name;
  415.         n = cn -> cn_Size - 4;
  416.         while (n > 0) {
  417.            ReadChunkBytes(xd -> iff, & type, sizeof(APTR));
  418.            n -= sizeof(APTR);
  419.            ReadChunkBytes(xd -> iff, & size, sizeof(APTR));
  420.            n -= sizeof(APTR);
  421.            name = (char *) malloc(size + 1);
  422.            ReadChunkBytes(xd -> iff, name, size);
  423.            name[size] = '\0';
  424.            n -= size;
  425.            if (size % 4 != 0) {
  426.           ReadChunkBytes(xd -> iff, devNull, 4 - (size % 4));
  427.           n -= 4 - (size % 4);
  428.            }
  429.  
  430.            xd_declareTypeAssoc(xd, name, NULL, type, xd -> ltypes);
  431.            free(name);
  432.         }
  433.      }
  434.       }
  435.       else {
  436.      xd_error(xd, XD_NO_ID_XFIE, NULL);
  437.       }
  438.  
  439.  
  440.       /*
  441.        ** Read the XUSE chunk
  442.        */
  443.       if (xd_skipToId(xd, ID_XUSE)) {
  444.      struct ContextNode *cn = CurrentChunk(xd -> iff);
  445.      if (! cn) {
  446.         xd_error(xd, XD_NO_ID_XUSE, NULL);
  447.         result = NULL;
  448.      }
  449.      else {
  450.         int n, length;
  451.         char *key, *value;
  452.         n = cn -> cn_Size - 4;
  453.         while (n > 0) {
  454.            /*
  455.         ** Read the key...
  456.         */ 
  457.            ReadChunkBytes(xd -> iff, & length, sizeof(length));
  458.            n -= sizeof(length);
  459.            key = (char *) malloc(length + 1);
  460.            ReadChunkBytes(xd -> iff, key, length);
  461.            n -= length;
  462.            key[length] = '\0';
  463.            if (length % 4 != 0) {
  464.          ReadChunkBytes(xd -> iff, devNull, 4 - (length % 4));
  465.          n -= 4 - (length % 4);
  466.           }
  467.  
  468.            /*
  469.         ** ... and the value
  470.         */ 
  471.            ReadChunkBytes(xd -> iff, & length, sizeof(length));
  472.            n -= sizeof(length);
  473.            value = (char *) malloc(length + 1);
  474.            ReadChunkBytes(xd -> iff, value, length);
  475.            n -= length;
  476.            value[length] = '\0';
  477.            if (length % 4 != 0) {
  478.           ReadChunkBytes(xd -> iff, devNull, 4 - (length % 4));
  479.           n -= 4 - (length % 4);
  480.            }
  481.            xd_declareShared(xd, key, value);
  482.         }  /* while n > 0 */
  483.      }
  484.       }
  485.       else {
  486.      xd_error(xd, XD_NO_ID_XUSE, NULL);
  487.       }
  488.    }
  489.    return result;
  490. }
  491.  
  492.  
  493. /***********************************************************************
  494.  * Public
  495.  ***********************************************************************/
  496.  
  497. int
  498. xd_Init(void)
  499. {
  500.    int result = 0;
  501.  
  502.    IFFParseBase = (struct IFFParseBase *) OpenLibrary("iffparse.library", 0L);
  503.    if (IFFParseBase == NULL) {
  504.       result = 1;
  505.    }
  506.  
  507.    return result;
  508. }
  509.  
  510. void
  511. xd_Uninit(Xd_Database xd)
  512. {
  513.    if (xd != NULL) {
  514.       xd_freeList(xd -> ltypes);
  515.       xd_freeList(xd -> lshared);
  516.       SAFEFREE(xd -> fileType);
  517.       SAFEFREE(xd -> application);
  518.       SAFEFREE(xd -> author);
  519.       SAFEFREE(xd -> version);
  520.       SAFEFREE(xd -> date);
  521.       SAFEFREE(xd);
  522.    }
  523.    if (IFFParseBase != NULL) {
  524.       CloseLibrary((struct Library *) IFFParseBase);
  525.       IFFParseBase = NULL;
  526.    }
  527. }
  528.  
  529. Xd_Error
  530. xd_ErrorCode(Xd_Database xd)
  531. {
  532.    return xd -> errorNumber;
  533. }
  534.  
  535. char *
  536. xd_ErrorString(Xd_Database xd)
  537. {
  538.    return "An error occured\n";
  539. }
  540.  
  541. void
  542. xd_Close(Xd_Database xd)
  543. {
  544.    PopChunk(xd -> iff);    /* pop the XCON chunk */
  545.    CloseIFF(xd -> iff);
  546.    Close(xd -> iff -> iff_Stream);
  547.    FreeIFF(xd -> iff);
  548. }
  549.  
  550. void
  551. xd_DeclareApplication(Xd_Database xd, char *application)
  552. {
  553.    xd -> application = strdup(application);
  554. }
  555.  
  556. void
  557. xd_DeclareAuthor(Xd_Database xd, char *author)
  558. {
  559.    xd -> author = strdup(author);
  560. }
  561.  
  562. void
  563. xd_DeclareVersion(Xd_Database xd, char *version)
  564. {
  565.    xd -> version = strdup(version);
  566. }
  567.  
  568. void
  569. xd_DeclareDate(Xd_Database xd, char *date)
  570. {
  571.    xd -> date = strdup(date);
  572. }
  573.  
  574.  
  575. char *
  576. xd_ReadType(Xd_Database xd)
  577. {
  578.    return xd -> fileType;
  579. }
  580.  
  581. char *
  582. xd_ReadApplication(Xd_Database xd)
  583. {
  584.    return xd -> application;
  585. }
  586.  
  587. char *
  588. xd_ReadAuthor(Xd_Database xd)
  589. {
  590.    return xd -> author;
  591. }
  592.  
  593. char *
  594. xd_ReadDate(Xd_Database xd)
  595. {
  596.    return xd -> date;
  597. }
  598.  
  599. char *
  600. xd_ReadVersion(Xd_Database xd)
  601. {
  602.    return xd -> version;
  603. }
  604.  
  605.  
  606. Xd_Database
  607. xd_Open(char *filename, Xd_Mode mode, char *fileType)
  608. {
  609.    BPTR f;
  610.    struct IFFHandle *iff;
  611.    Xd_Database result;
  612.  
  613.    /* Try to open the file */
  614.    f = Open(filename, (mode == XD_WRITE ? MODE_NEWFILE : MODE_OLDFILE));
  615.    if (f == NULL) {
  616.       return NULL;
  617.    }
  618.  
  619.    /* Allocate the value we will return */
  620.    NEW(result, struct _Xd_Database);
  621.    memset(result, 0, sizeof(*result));
  622.  
  623.    /* ... and fill it */
  624.    NEW(result -> ltypes, struct List);
  625.    NEW(result -> lshared, struct List);
  626.    NewList(result -> ltypes);
  627.    NewList(result -> lshared);
  628.  
  629.    iff = result -> iff = AllocIFF();
  630.    if (iff == NULL) {
  631.       xd_error(result, XD_NO_ALLOC_IFF, NULL);
  632.       result = NULL;
  633.    }
  634.    else {
  635.       iff -> iff_Stream = f;
  636.  
  637.       InitIFFasDOS(iff);
  638.  
  639.       if (fileType == NULL || fileType[0] == '\0') {
  640.      xd_error(result, XD_NO_VALID_FILETYPE, NULL);
  641.      result = NULL;
  642.       }
  643.       else {
  644.      result -> fileType = strdup(fileType);
  645.      if (mode == XD_READ) {
  646.         if (OpenIFF(iff, IFFF_READ)) {
  647.            xd_error(result, XD_NO_OPENIFF_READ, NULL);
  648.            result = NULL;
  649.         }
  650.         else {
  651.           result = xd_readHeader(result, fileType);
  652.        }
  653.      }
  654.      
  655.      else if (mode == XD_WRITE) {
  656.         if (OpenIFF(iff, IFFF_WRITE)) {
  657.            xd_error(result, XD_NO_OPENIFF_WRITE, NULL);
  658.            result =  NULL;
  659.         }
  660.         else {
  661.            PushChunk(iff, ID_XDAT, ID_LIST, IFFSIZE_UNKNOWN);
  662.            PushChunk(iff, ID_XSHA, ID_PROP, IFFSIZE_UNKNOWN);
  663.         }
  664.      }
  665.       }
  666.    }
  667.    
  668.    return result;
  669. }
  670.  
  671. void
  672. xd_DeclareSharedString(Xd_Database xd, char *field, char *value)
  673. {
  674.    struct TypeAssoc *ta;
  675.    NEW(ta, struct TypeAssoc);
  676.    ta -> node.ln_Name = strdup(field);
  677.    ta -> type = XD_STRING;
  678.    ta -> value = strdup(value);
  679.    AddTail(xd -> lshared, (struct Node *) ta);
  680. }
  681.  
  682. void
  683. xd_ReadSharedString(Xd_Database xd, char *field, char **value)
  684. {
  685.  
  686.    struct TypeAssoc *ta = (struct TypeAssoc *) FindName(xd -> lshared, field);
  687.    if (ta)
  688.      *value = ta -> value;
  689.    else
  690.      *value = NULL;
  691. }
  692.  
  693. int
  694. xd_DeclareField(Xd_Database xd, char *field, Xd_Type type)
  695. {
  696.    int result = 0;
  697.  
  698.    if (! xd_declareTypeAssoc(xd, field, NULL, type, xd -> ltypes)) {
  699.       xd_error(xd, XD_NO_NEW_FIELD_ALLOWED, field);
  700.       result = 1;
  701.    }
  702.  
  703.    return result;
  704.  
  705. }
  706.  
  707. void
  708. xd_AssignField(Xd_Database xd, char *field, void *value)
  709. /**@@**/
  710. {
  711.    struct TypeAssoc *type = xd_findType(xd -> ltypes, field);
  712.  
  713.    if (type) {
  714.       if (type -> type == XD_STRING)
  715.         type -> value = strdup((char *) value);
  716.       else if (type -> type == XD_INTEGER)
  717.         type -> value = value;
  718.       else
  719.     xd_error(xd, XD_UNKNOWN_FIELD_TYPE, NULL);
  720.    }
  721.    else
  722.      xd_error(xd, XD_UNKNOWN_FIELD, field);
  723. }
  724.  
  725. void
  726. xd_WriteRecord(Xd_Database xd)
  727. {
  728.    struct Node *node;
  729.    struct List *list;
  730.    int size = 0;
  731.  
  732.    if (xd -> writeOccured == 0) {
  733.       xd_writeHeader(xd);
  734.       xd -> writeOccured = 1;
  735.    }
  736.  
  737.    /*
  738.     ** Writing the record means opening an XREC chunk and
  739.     ** dumping the data
  740.     */
  741.  
  742.    PushChunk(xd -> iff, ID_XCON, ID_XREC, IFFSIZE_UNKNOWN);
  743.    list = xd -> ltypes;
  744.    for (node = list -> lh_Head; node -> ln_Succ; node = node -> ln_Succ) {
  745.       struct TypeAssoc *ta = (struct TypeAssoc *) node;
  746.  
  747.       /* Calculate the size to put here */
  748.       if (ta -> type == XD_STRING) size = strlen(ta -> value);
  749.       else if (ta -> type == XD_INTEGER) size = sizeof(int);
  750.  
  751.       /* ... and then write the chunk */
  752.       xd_writeChunk(xd -> iff, ta -> value, size);
  753.  
  754.    }
  755.    PopChunk(xd -> iff);   /* popping xrec */
  756. }
  757.  
  758.  
  759. int
  760. xd_NextRecord(Xd_Database xd)
  761. {
  762.    struct Node *node;
  763.    struct List *list = xd -> ltypes;
  764.    int error;
  765.    int result;
  766.  
  767. /*
  768.  ** Basically, this function skips to the next XREC chunk. It
  769.  ** reads its length, and then the fields in the same order as
  770.  ** they were found declared in the XFIE chunk. They are stored
  771.  ** in the xd -> ltypes list variable, where they can be retrieved
  772.  ** individually later on with xd_ReadField()
  773.  */
  774.  
  775.    /* First skip to first XREC hunk */
  776.    if (! xd_skipToType(xd, ID_XCON)) {
  777.       xd_error(xd, XD_NO_TYPE_XCON, NULL);
  778.       return 0;
  779.    }
  780.  
  781.    StopChunk(xd -> iff, ID_XCON, ID_XREC);
  782.    error = ParseIFF(xd -> iff, IFFPARSE_SCAN);
  783.    if (error == IFFERR_EOF) {
  784.       result = 0;
  785.    }
  786.    else {
  787.       char devNull[10];   /* buffer to hold pad bytes */
  788.       result = 1;
  789.       for (node = list -> lh_Head; node -> ln_Succ; node = node -> ln_Succ) {
  790.      struct TypeAssoc *ta = (struct TypeAssoc *) node;
  791.      int n;
  792.      void *value;
  793.      ReadChunkBytes(xd -> iff, & n, sizeof(n));
  794.      value = malloc(n + 1);
  795.      ReadChunkBytes(xd -> iff, value, n);
  796.      if (n % 4 != 0)
  797.        ReadChunkBytes(xd -> iff, devNull, 4 - (n % 4));
  798.      if (ta -> value) free(ta -> value);   /* free previous value */
  799.      ta -> value = value;
  800.      if (ta -> type == XD_STRING)
  801.        ((char *) ta -> value)[n] = '\0';
  802.       }
  803.    }
  804.  
  805.    return result;
  806. }
  807.  
  808. int
  809. xd_EndOfFile(Xd_Database xd)
  810. /* Return 1 if the end of file is reached, 0 otherwise */
  811. {
  812.    return (ParseIFF(xd -> iff, IFFPARSE_RAWSTEP) == IFFERR_EOF);
  813. }
  814.  
  815. int
  816. xd_ReadField(Xd_Database xd, char *field, Xd_Type type, void *dest)
  817. /* Read a field into the variable dest */
  818. /* Return : 1 if the field doesn't exist */
  819. {
  820.    int result = 0;
  821.    struct TypeAssoc *ta;
  822.  
  823.    ta = (struct TypeAssoc *) FindName(xd -> ltypes, field);
  824.    if (ta == NULL) {
  825.       xd_error(xd, XD_UNKNOWN_FIELD, field);
  826.       result = 1;
  827.    }
  828.      else {
  829.       type = ta -> type;
  830.       if (type == XD_INTEGER) {
  831.      int n = * (int *) ta -> value;
  832.      * (int *) dest = n;
  833.       }
  834.       else if (type == XD_STRING) {
  835.      char *string = strdup(ta -> value);
  836.      * (char **) dest = string;
  837.       }
  838.    }
  839.  
  840.    return result;
  841. }
  842.